<?php namespace sacvoyage\storage;

require_once("sacvoyage/Pathfinder.php");
use \sacvoyage\Pathfinder;

require_once 'sacvoyage/Object.php';
use sacvoyage\Object;
/*
 * implements select functionality. selects only needed paths from document
 *
 */
class Selector implements \Iterator {

	/*
	 * if keyIterator not specified, all will be selected
	 * if jsonSelect not specified, all 
	 */
	public function __construct(&$table, $keyIterator=null, 
	                            $select=null, $includeRev=false) {
		$this->_table=&$table;
		$this->_keyIterator=&$keyIterator;
		$this->_select=$select;
		$this->_includeRev=$includeRev;
		$this->_selectPaths=$this->_getSelectPaths($select);
	}

	public function __destruct() {
		if($this->_queryResult) {
			$this->_queryResult->free();
		}
	}

	public function rewind() {
		if($this->_keyIterator) {
			$this->_keyIterator->rewind();
		}
		if($this->_queryResult) {
			$this->_queryResult->free();
			$this->_queryResult=null;
		}
		$this->_executeQuery();
		if($this->_queryResult) {
			$this->_currentRow=$this->_queryResult->fetch_assoc();
			$this->_currentPosition=-1;
			$this->next();
		} else {
			$this->_currentRow=null;
		}

	}

	public function key() {
		return $this->_currentPosition;
	}

	public function current() {
		return $this->_currentDocument;
	}
	
	public function next() {
		if($this->_currentRow) {
			$this->_currentPosition++;
			$_id=$this->_currentRow['_id'];
			$_rev=$this->_currentRow['_rev'];
			$this->_currentDocument=new Object();
			$finder=new Pathfinder($this->_currentDocument);

			do {
				if($this->_currentRow['path']!='._rev'
				|| $this->_includeRev
				|| ($this->_selectPaths && in_array('._rev', $this->_selectPaths))) {
					
					$finder[$this->_currentRow['path']]=json_cast_type(
						$this->_currentRow['value'],
						$this->_currentRow['type']
					);
				}
			} while(($this->_currentRow=$this->_queryResult->fetch_assoc())
			     && ($this->_currentRow['_id']==$_id)
			     && ($this->_currentRow['_rev']==$_rev));
			
		} else {
			$this->_currentKey=null;
			$this->_currentDocument=null;
		}
	}

	public function valid() {
		return $this->_currentDocument ? true : false;
	}

	private $_table;
	private $_keyIterator;
	private $_select;
	private $_queryResult;
	private $_currentKey;
	private $_currentDocument;



	private function _executeQuery() {
		// TODO: split keys into 10000-groups
		if($this->_queryResult) {
			$this->_queryResult->free();
		}

		// if select-paths are specified, format a sql-where clause
		$wherePaths=$this->_getSQLWherePaths($this->_selectPaths);

		// make a query
		$alias=&$this->_table->getAlias();
		if($this->_keyIterator) {
			if(!$this->_keyIterator->valid()) {
				return;
			}
			$tempTableName='temp_table_'.random_string();
			$this->_table->query(
				"CREATE TEMPORARY TABLE $tempTableName ".
				"(rank INT, _id TEXT, _rev TEXT)"
			);
			
			$order=0;
			$insertValues=array();
			foreach($this->_keyIterator as $key) {
				$_id=$this->_table->escape($key->_id);
				$_rev=$this->_table->escape($key->_rev);
				$insertValues[]="($order, '$_id', '$_rev')";
				$order++;
			}
			if($insertValues) {
				$this->_table->query(
					"INSERT INTO $tempTableName (rank, _id, _rev) ".
					"VALUES ".implode(', ', $insertValues)
				);
			}
			
			$sql="SELECT $alias._id, $alias._rev, path, type, value ".
		         "FROM ($tempTableName INNER JOIN ".
			     "{$this->_table->getExpression()} AS $alias ".
			     "ON {$tempTableName}._id={$alias}._id ".
			     "AND {$tempTableName}._rev={$alias}._rev) ".	
			     ($wherePaths ? "WHERE $wherePaths " : "").
			     "ORDER BY $tempTableName.rank, $alias._id, $alias._rev";
			
			$this->_queryResult=$this->_table->query($sql);
			$this->_table->query("DROP TABLE $tempTableName");
		} else {
			$sql="SELECT _id, _rev, path, type, value ".
			     "FROM {$this->_table->getExpression()} AS $alias ".
			     ($wherePaths ? "WHERE $wherePaths " : "").
			     "ORDER BY $alias._id, $alias._rev";
			
			$this->_queryResult=$this->_table->query($sql);
		}
		
	}

	private function _getSelectPaths($select) {
		if($select) {
			$finder=new Pathfinder($select);
			return array_keys(array_filter($finder->get()));
		} else {
			return null;
		}
	}

	/*
	 * parse select clause
	 * and ...
	 *
	 */
	private function _getSQLWherePaths($selectPaths) {
		if($selectPaths!==null) {
			return "path IN ('".implode(
				"', '", 
				array_map(
					array($this->_table, 'escape'),
					$selectPaths
				)
			)."')";
		} else {
			return null;
		}
	}

}


?>
